package org.ffmmx.token

import org.apache.commons.codec.binary.Base64
import org.apache.commons.codec.digest.Md5Crypt
import org.joda.time.DateTime

/**
  * 口令信息
  *
  * @param key         唯一键
  * @param jsonProfile json格式的权限之类的用户附加信息
  * @param expireTime  过期时间
  * @param magicStr    生成签名编码串
  */
case class Token(key: String, jsonProfile: String, expireTime: DateTime, magicStr: String = "!@#$%^123456asdfgh") {
  /**
    * 生成TOKEN串
    *
    * @return TOKEN字符串
    */
  def generateToken: String = s"$generateProfile${Token.splitSep}$generateSign"

  /**
    * 生成签名
    *
    * @return 签名字符串
    */
  def generateSign: String = Md5Crypt.md5Crypt(s"$key$magicStr$jsonProfile$magicStr${expireTime.getMillis}".getBytes())

  /**
    * 生成内容字符串
    *
    * @return 内容字符串
    */
  def generateProfile: String = Base64.encodeBase64String(s"$key${Token.profileSep}$jsonProfile${Token.profileSep}${expireTime.getMillis}".getBytes())

}

/**
  * 刷新口令
  *
  * @param token      原口令串
  * @param expireTime 过期时间
  */
case class RefreshToken(token: String, expireTime: DateTime) {
  val magicStr = "&*()_7890-ZXCVB"

  /**
    * 生成刷新口令
    *
    * @return 刷新口令
    */
  def generateRefreshToken: String = {
    if (!Token.validateToken(token)) {
      throw new RuntimeException("原口令检查失败")
    }
    Token.parseToken(token)._1.copy(expireTime = expireTime, magicStr = magicStr).generateToken
  }


}

/**
  *
  */
object Token {
  type Sign = String

  protected val profileSep = ";"
  protected val splitSep = "."

  /**
    * 生成TOKEN串
    *
    * @param key         唯一标识
    * @param jsonProfile json格式的用户附加信息
    * @param expireTime  过期时间
    * @return TOKEN
    */
  def generateToken(key: String, jsonProfile: String, expireTime: DateTime): String = Token(key, jsonProfile, expireTime).generateToken

  /**
    * 检查TOKEN
    *
    * @param token TOKEN串
    * @return 通过 True 不通过 False
    */
  def validateToken(token: String): Boolean = {
    val tokenT = parseToken(token)._1
    if (tokenT.generateToken == token) {
      tokenT.expireTime.isAfterNow
    } else {
      false
    }
  }

  /**
    * 生成刷新口令串
    *
    * @param token      原TOKEN串
    * @param exprieTime 过期时间
    * @return 刷新口令串
    */
  def generateRefreshToken(token: String, exprieTime: Option[DateTime] = None): String = RefreshToken(token, exprieTime.getOrElse(DateTime.now().plusDays(1))).generateRefreshToken

  /**
    * 检查刷新口令
    *
    * @param token        原token串
    * @param refreshToken 刷新口令串
    * @return
    */
  def validateRefreshToken(token: String, refreshToken: String): Boolean = {
    val refreshTokenT = parseToken(refreshToken)._1
    if (RefreshToken(token, refreshTokenT.expireTime).generateRefreshToken == refreshToken) {
      refreshTokenT.expireTime.isAfterNow
    } else {
      false
    }
  }

  /**
    * 解析口令
    *
    * @param token TOKEN
    * @return TOKEN内容对象 和 签名
    */
  def parseToken(token: String): (Token, Sign) = {
    token.split(Token.splitSep) match {
      case x if x.length == 2 =>
        (parseProfile(x(0)), x(1))
      case _ =>
        throw new RuntimeException("token格式不正确")
    }
  }

  /**
    * 解析内容部分
    *
    * @param profile 内容部分字符串
    * @return TOKEN内容对象
    */
  def parseProfile(profile: String): Token = new String(Base64.decodeBase64(profile)).split(profileSep) match {
    case x if x.length == 3 =>
      Token(x(0), x(1), new DateTime(x(2).toLong))
    case _ =>
      throw new RuntimeException("profile 格式不正确")
  }


}
